exportfunctionrender( element: React$Element<any>, container: Container, callback: ?Function, ) { // 用于抛出错误的, 就是判断这个container是不是找得到 invariant( isValidContainer(container), 'Target container is not a DOM element.', ); if (__DEV__) { const isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined; if (isModernRoot) { console.error( 'You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.createRoot(). This is not supported. ' + 'Did you mean to call root.render(element)?', ); } } // 这里面涉及到了fiber的一些架构,开始补坑 return legacyRenderSubtreeIntoContainer( null, element, container, false, callback, ); }
最后return的这个函数有点意思。
源码攻读
legacyRenderSubtreeIntoContainer
我们首先翻译一下这个函数的名字,进行驼峰拆分:
Google翻译:legacy Render Subtree Into Container(旧版渲染子树放入容器
functionlegacyCreateRootFromDOMContainer( container: Container, forceHydrate: boolean, ): RootType{ // 如果forceHydrate是false,就会调用后面的方法。 const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content. if (!shouldHydrate) { let warned = false; let rootSibling; // 这个循环,是为了清理所有的内容,然后找到我们要渲染的最初的根div。通常我们的html会有一个 // <div id="root"></div> 为的就是保障他的纯净。 while ((rootSibling = container.lastChild)) { if (__DEV__) { if ( !warned && rootSibling.nodeType === ELEMENT_NODE && (rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME) ) { warned = true; console.error( 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.', ); } } container.removeChild(rootSibling); } } if (__DEV__) { if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) { warnedAboutHydrateAPI = true; console.warn( 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v18. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.', ); } }
// Cyclic construction. This cheats the type system right now because // stateNode is any. // 通过createHostRootFiber方法创建fiber tree的根结点,即rootFiber // fiber节点也会像DOM树结构一样形成一个fiber tree单链表树结构 // 每个DOM节点或者组件都会生成一个与之对应的fiber节点,在后续的调和(reconciliation)阶段起着至关重要的作用 const uninitializedFiber = createHostRootFiber(tag, strictModeLevelOverride); // 创建完rootFiber之后,会将fiberRoot的实例的current属性指向刚创建的rootFiber root.current = uninitializedFiber; // 同时rootFiber的stateNode属性会指向fiberRoot实例,形成相互引用 uninitializedFiber.stateNode = root;
if (enableProfilerTimer && isDevToolsPresent) { // Always collect profile timings when DevTools are present. // This enables DevTools to start capturing timing at any point– // Without some nodes in the tree having empty base times. mode |= ProfileMode; } // export const HostRoot = 3; return createFiber(HostRoot, null, null, mode); }
createFiber
也是包一个壳子。主要还是为了创建FiberNode。
1 2 3 4 5 6 7 8 9
const createFiber = function( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ): Fiber{ // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors returnnew FiberNode(tag, pendingProps, key, mode); };
if (enableProfilerTimer) { // Note: The following is done to avoid a v8 performance cliff. // // Initializing the fields below to smis and later updating them with // double values will cause Fibers to end up having separate shapes. // This behavior/bug has something to do with Object.preventExtension(). // Fortunately this only impacts DEV builds. // Unfortunately it makes React unusably slow for some applications. // To work around this, initialize the fields below with doubles. // // Learn more about this here: // https://github.com/facebook/react/issues/14365 // https://bugs.chromium.org/p/v8/issues/detail?id=8538 this.actualDuration = Number.NaN; this.actualStartTime = Number.NaN; this.selfBaseDuration = Number.NaN; this.treeBaseDuration = Number.NaN;
// It's okay to replace the initial doubles with smis after initialization. // This won't trigger the performance cliff mentioned above, // and it simplifies other profiler code (including DevTools). this.actualDuration = 0; this.actualStartTime = -1; this.selfBaseDuration = 0; this.treeBaseDuration = 0; }
if (__DEV__) { // This isn't directly used but is handy for debugging internals: this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugNeedsRemount = false; this._debugHookTypes = null; if (!hasBadMapPolyfill && typeofObject.preventExtensions === 'function') { Object.preventExtensions(this); } } }